package com.huatai.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huatai.common.entity.UserInfo;
import com.huatai.common.error.exception.ServiceException;
import com.huatai.common.util.AuthUtil;
import com.huatai.common.util.CollectionUtil;
import com.huatai.common.util.StringUtil;
import com.huatai.user.entity.Permission;
import com.huatai.user.entity.RolePermission;
import com.huatai.user.mapper.PermissionMapper;
import com.huatai.user.service.PermissionService;
import com.huatai.user.service.RolePermissionService;
import com.huatai.user.vo.PermissionVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


import static com.huatai.user.constant.RedisConstant.PERMISSION_DATA;
import static com.huatai.user.constant.RedisConstant.PERMISSION_LOCK;

/**
 * @program: ht-system
 * @description: 资源权限
 * @author: songxiude
 * @create: 2023-02-09 16:17
 **/
@Service
@Slf4j
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {
	@Autowired
	private RolePermissionService rolePermissionService;
	@Autowired
	private RedissonClient redissonClient;

	@Autowired
	private StringRedisTemplate redisTemplate;

	@Lazy
	@Autowired
	private PermissionServiceImpl permissionService;

	/**
	 * 资源权限新增修改
	 *
	 * @param permission
	 * @return
	 */
	public boolean savePermission(Permission permission) {
		//获取分布式锁
		RLock lock = redissonClient.getLock(PERMISSION_LOCK);
		lock.lock();
		boolean rel;
		try {
			//获取登录人信息
			UserInfo userInfo = AuthUtil.getUser();
			permission.setUpdateUserid(userInfo.getUserId());
			if (permission.getId() == null || permission.getId().longValue() <= 0) {
				permission.setId(IdWorker.getId());
				permission.setCreateUserid(userInfo.getUserId());
			}
			//判断是不是系统级别菜单，如果是只能是顶级并且不能到其他子级（4代表系统）
			if (null == permission.getParentId()) {
				permission.setParentId(0l);
			}
			if (permission.getPermissionType().equals(4) && !permission.getParentId().equals(0L)) {
				throw new ServiceException("系统级别菜单不能成为其他菜单子级");
			}
			rel = saveOrUpdate(permission);
			//判断是否设置为首页
			if (permission.getIsHomepage().intValue() == 1) {
				permissionService.updateHome(permission);
			}
			if (rel) {
				redisTemplate.delete(PERMISSION_DATA);
			}
		} finally {
			//释放锁
			lock.unlock();
		}
		return rel;
	}

	/**
	 * 修改首页
	 *
	 * @param permission
	 */
	@Async("threadPoolExecutor")
	public void updateHome(Permission permission) {
		List<Long> ids = new ArrayList();
		List<Permission> permissions = baseMapper.selectList(null);
		//获取父级节点
		List<Long> parentPermissions = getParentPermissions(permission, permissions, ids);
		//获取子节点
		List<Long> allChildrenIds = getAllChildrenIds(parentPermissions.get(0), permissions);
		allChildrenIds.add(parentPermissions.get(0));
		LambdaUpdateWrapper<Permission> updateWrapper = Wrappers.<Permission>lambdaUpdate();
		updateWrapper.in(CollectionUtil.isNotEmpty(allChildrenIds), Permission::getId, allChildrenIds)
			.ne(Permission::getId, permission.getId())
			.set(Permission::getIsHomepage, 0);
		update(null, updateWrapper);
	}

	/**
	 * 获取所有子节点id
	 *
	 * @param parentId
	 * @param nodes
	 * @return
	 */
	public static List<Long> getAllChildrenIds(Long parentId, List<Permission> nodes) {
		List<Long> childrenIds = new ArrayList<>();
		for (Permission node : nodes) {
			if (node.getParentId() != null && node.getParentId().longValue() == parentId.longValue()) {
				childrenIds.add(node.getId());
				childrenIds.addAll(getAllChildrenIds(node.getId(), nodes));
			}
		}
		return childrenIds;
	}

	/**
	 * 获取所有父节点id
	 *
	 * @param permission
	 * @param nodeList
	 * @param parentnode
	 * @return
	 */
	public static List<Long> getParentPermissions(Permission permission, List<Permission> nodeList, List<Long> parentnode) { //参数：要查询父节点的实体，存放所有节点的列表，最终存放父节点的列表
		if (permission.getParentId().longValue() == 0) { //parentId == 0时表示已经查到了最上级，跳出递归
			Collections.reverse(parentnode); //列表倒序，可以不用
			return parentnode;
		}
		List<Permission> nodelist = nodeList.stream().filter(x -> Objects.equals(x.getId(), permission.getParentId()) //递归查询当前节点的父节点（唯一）
		).collect(Collectors.toList());
		parentnode.add(nodelist.get(0).getId()); //将查询到的父节点放入集合并返回
		return getParentPermissions(nodelist.get(0), nodeList, parentnode); //递归
	}

	/**
	 * 删除资源权限
	 *
	 * @param permissionId
	 * @return
	 */
	@Transactional(rollbackFor = {Exception.class})
	public boolean delPermission(Long permissionId) {
		//获取分布式锁
		RLock lock = redissonClient.getLock(PERMISSION_LOCK);
		lock.lock();
		boolean rel;
		try {
			//判断是否包含子级如果有不能删除
			//Permission permission = getOne(Wrappers.<Permission>lambdaQuery().eq(Permission::getParentId, permissionId));
			//List<Permission> list = list(Wrappers.<Permission>lambdaQuery().eq(Permission::getParentId, permissionId));
			long count = count(Wrappers.<Permission>lambdaQuery().eq(Permission::getParentId, permissionId));
			if (count > 0) {
				throw new ServiceException("该资源包含子级不能删除，请从最子级向上删除");
			}
			//删除资源权限
			boolean remove = removeById(permissionId);
			if (remove) {
				//删除缓存信息
				redisTemplate.delete(PERMISSION_DATA);
				//删除角色里面的资源权限
				rolePermissionService.remove(Wrappers.<RolePermission>lambdaQuery().eq(RolePermission::getPermissionId, permissionId));
			}
			rel = remove;
		} finally {
			lock.unlock();
		}

		return rel;
	}

	/**
	 * 获取资源权限列表
	 *
	 * @return
	 */
	public List<PermissionVo> getPermissions() {
		RLock lock = redissonClient.getLock(PERMISSION_LOCK);
		lock.lock();
		List<PermissionVo> permissionVoList;
		try {
			//如果缓存已经存在不查询数据库
			String pstr = redisTemplate.opsForValue().get(PERMISSION_DATA);
			if (StringUtil.isNotBlank(pstr)) {
				List<PermissionVo> voList = JSON.parseObject(pstr, new TypeReference<List<PermissionVo>>() {
				});
				permissionVoList = voList;
			} else {
				//缓存没有查询存在到redis
				permissionVoList = baseMapper.getPermissionList(0L);
//				List<PermissionVo> permissions = baseMapper.getPermissions();
//				//递归获取新
//				permissionVoList = getTrees(0L, permissions);
				redisTemplate.opsForValue().set(PERMISSION_DATA, JSON.toJSONString(permissionVoList), 86400, TimeUnit.SECONDS);
			}
		} finally {
			lock.unlock();
		}

		return permissionVoList;

	}

	/**
	 * 递归获取子级列表
	 *
	 * @param id
	 * @param trees
	 * @return
	 */
	private List<PermissionVo> getTrees(Long id, List<PermissionVo> trees) {
		List<PermissionVo> list = new ArrayList<>();
		for (PermissionVo tree : trees) {
			if (tree.getParentId().longValue() == id.longValue()) {
				tree.setChildren(getTrees(tree.getId(), trees));
				list.add(tree);
			}
		}
		return list;
	}

}
